/**
 * 
 */
package com.novartis.nims.trialsubmitter;

import java.io.File;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;

import com.novartis.nims.util.DicomHelper;
import com.pixelmed.dicom.AttributeList;

/**
 * @author Josh Snyder
 *
 */
class UnsortedTrialReader extends TrialReader {
	
	private Map<String, Integer> mScanRepeatKeyUsedForFileScanKey;
	private Map<String, Integer> mNextRepeatKeyForScanKey;
	private Map<String, String> mFileKeyNameMap;

	UnsortedTrialReader(TrialSubmitter ts, URI trialLocation) {
		super(ts, trialLocation);
	}

	/* (non-Javadoc)
	 * @see com.novartis.nims.trialsubmitter.TrialReader#initialize()
	 */
	@Override
	protected void initialize() {
		mScanRepeatKeyUsedForFileScanKey = new HashMap<String, Integer>();
		mNextRepeatKeyForScanKey = new HashMap<String, Integer>();
		mFileKeyNameMap = new HashMap<String, String>();
	}

	/* (non-Javadoc)
	 * @see com.novartis.nims.trialsubmitter.TrialReader#processFile(java.io.File)
	 */
	@Override
	protected SubmissionKey processFile(File file, URI relativeLocation) throws TrialReaderException {
		String trialKey = null;
		String subjectKey = null;
		String visitKey = null;
		String visitNumber = null;
		String visitRepeatKey = null;
		String scanKey = null;
		String scanRepeatKey = null;

		AttributeList attributes = DicomHelper.readDICOMAttributes(file, this.getAllowBadPixelData());
		if (attributes == null && (! getExpectNonDicom())) {
			abortOrWarn(getAbortOnUnspecifiedFileError(), file.getAbsolutePath() +
				" couldn't be opened as a DICOM file.");
			return null;
		}
		
		String[] components = relativeLocation.getPath().split("/");
		int numDirectories = components.length - 1; //remove trailing file component from directory count
		int trialIndex = 0;
		int subjectIndex = trialIndex + 1;
		if (numDirectories < subjectIndex + 1) {
			throw new TrialReaderException(file.getAbsolutePath() + " does not have trial and subject parent directories. " +
					"Try re-organizing the input files and running again.");
		}
			
		trialKey = components[trialIndex];
		subjectKey = components[subjectIndex];
		int visitIndex = subjectIndex + 1;
		int scanIndex = visitIndex + 1;
		int scanRepeatIndex = scanIndex + 1;
		if (getVisitsOrganized() && (numDirectories < visitIndex + 1)) {
			throw new TrialReaderException(file.getAbsolutePath() + " does not have visit directories. " +
				"Try re-organizing the input files and running again.");
		}
		if (getScansOrganized() && (numDirectories < scanIndex + 1)) {
			throw new TrialReaderException(file.getAbsolutePath() + " does not have scan directories. " +
				"Try re-organizing the input files and running again.");
		}
		
		// determine visit key and visit repeat key
		if (getVisitsOrganized() || getExpectNonDicom()) {			
			if (numDirectories < visitIndex + 1) {
				throw new TrialReaderException(file.getAbsolutePath() + " does not have trial, subject, and visit parent directories. " +
						"Try re-organizing the input files and running again.");
			}
			
			scanIndex = visitIndex + 1;
			
			StringBuilder parsedVisitName = new StringBuilder();
			StringBuilder parsedVisitNumber = new StringBuilder();
			StringBuilder parsedRepeatVisitNumber = new StringBuilder();
			if (! this.parseVisitString(components[visitIndex], parsedVisitName, parsedVisitNumber, parsedRepeatVisitNumber)) {
				throw new TrialReaderException(file.getAbsolutePath() + " does not have a valid visit directory name. " +
						"Try making sure the visit name, number and/or repeat number are present and separated " +
						"correctly.");
			}
			visitKey = parsedVisitName.toString();
			visitNumber = parsedVisitNumber.toString();
			visitRepeatKey = parsedRepeatVisitNumber.toString();
			
			if (visitRepeatKey.equalsIgnoreCase("")) {
				visitRepeatKey = null;
			}

		} else {
			String fileVisitKey = DicomHelper.getAttributeValue(attributes, getVisitKey());
			if (fileVisitKey == null) {
				abortOrWarn(getAbortOnFileMissingVisitKey(), file.getAbsolutePath() +
						" missing DICOM attribute " + getVisitKey().toString() + ".");
				return null;
			} else if (fileVisitKey.isEmpty()) {
				abortOrWarn(getAbortOnFileMissingVisitKey(), file.getAbsolutePath() +
						" has empty DICOM attribute " + getVisitKey().toString() + ".");
				return null;
			}
			
			// since the same key value is used to identify a file's visit in both
			// our dicom model and our submission model, the repeat key doesn't
			// need to vary.
			visitKey = fileVisitKey;
			visitNumber = "";
			visitRepeatKey = null;
		}

		
		// determine scan key
		if (getScansOrganized() || getExpectNonDicom()) {
			if (numDirectories < scanIndex + 1) {
				throw new TrialReaderException(file.getAbsolutePath() + " does not have trial, subject, visit, and scan parent directories. " +
						"Try re-organizing the input files and running again.");
			}
			
			scanKey = components[scanIndex];
		} else {
			String fileScanName = DicomHelper.getAttributeValue(attributes, getScanName());
			if (fileScanName == null) {
				abortOrWarn(getAbortOnFileMissingScanName(), file.getAbsolutePath() +
						" missing DICOM attribute " + getScanName().toString() + ".");
				return null;
			} else if (fileScanName.isEmpty()) {
				abortOrWarn(getAbortOnFileMissingScanName(), file.getAbsolutePath() +
						" has empty DICOM attribute " + getScanName().toString() + ".");
				return null;
			}
			scanKey = fileScanName;
		}
		
		
		// determine scan repeat key
		if (getScansOrganized() && getExpectNonDicom() && (attributes == null)) {
			if (numDirectories < scanRepeatIndex + 1) { //if there's no scan repeat directory
				scanRepeatKey = "1";
			} else {
				scanRepeatKey = components[scanRepeatIndex];
			}
		} else {
			String fileScanKey = DicomHelper.getAttributeValue(attributes, getScanKey());
			if (fileScanKey == null) {
				abortOrWarn(getAbortOnFileMissingScanKey(), file.getAbsolutePath() +
						" missing DICOM attribute " + getScanKey().toString() + ".");
				return null;
			} else if (fileScanKey.isEmpty()) {
				abortOrWarn(getAbortOnFileMissingScanKey(), file.getAbsolutePath() +
						" has empty DICOM attribute " + getScanKey().toString() + ".");
				return null;
			}
		
			String baseCompositeKey = subjectKey+visitKey+visitRepeatKey;
			String scanCompositeKey = baseCompositeKey+scanKey;
			String fileScanCompositeKey = baseCompositeKey+fileScanKey;
			Integer scanRepeatNum = mScanRepeatKeyUsedForFileScanKey.get(fileScanCompositeKey);
			if (scanRepeatNum == null) {
				scanRepeatNum = mNextRepeatKeyForScanKey.get(scanCompositeKey);
				if (scanRepeatNum == null) scanRepeatNum = new Integer(1);
				mScanRepeatKeyUsedForFileScanKey.put(fileScanCompositeKey, scanRepeatNum);
				mNextRepeatKeyForScanKey.put(scanCompositeKey, new Integer(scanRepeatNum + 1));
			}

			scanRepeatKey = String.valueOf(scanRepeatNum);	

		
			//determine file key
			String fileKey = DicomHelper.getAttributeValue(attributes, getFileKey());
			if (fileKey == null) {
				abortOrWarn(getAbortOnFileMissingFileKey(), file.getAbsolutePath() +
						" missing DICOM attribute " + getFileKey().toString() + ".");
				return null;
			} else if (fileKey.isEmpty()) {
				abortOrWarn(getAbortOnFileMissingFileKey(), file.getAbsolutePath() +
						" has empty DICOM attribute " + getFileKey().toString() + ".");
				return null;
			}


			String previousFileName = mFileKeyNameMap.put(fileKey, file.getAbsolutePath());
			if (previousFileName != null) {
				abortOrWarn(getAbortOnFileWithDuplicateFileKey(), file.getAbsolutePath() +
						" contains duplicate SOP Instance UID " + fileKey +	" already found in file " +
						previousFileName);
				return null;
			}
		}	
		
		
		SubmissionKey fullKey = new SubmissionKey(
				trialKey,
				subjectKey,
				visitKey,
				visitRepeatKey,
				scanKey,
				scanRepeatKey);
		
		fullKey.setVisitNumber(visitNumber);
		
		return fullKey;
	}
	
	private void abortOrWarn(boolean abort, String message) throws TrialReaderException {
		if (abort) {
			throw new TrialReaderException(message);
		} else {
			mLogger.warn(message);
		}
	}

}
